/*******************************************************************************
 * Copyright (c) 2001, 2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.syncinfo;

import java.io.Externalizable;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Date;

import net.sourceforge.perforce.core.internal.Policy;

/**
  * @version $Revision: 150 $
  * @author  <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class ResourceSyncInfo implements Externalizable
{
	// ---------------------------------------------------------------------------- Public Constants
	
	/** Indicating non existing resource sync info. */
	public static final ResourceSyncInfo NULL_INFO = new ResourceSyncInfo(0x0000);	
	/** Constant for the add action. */
	public static final String ACTION_ADD    = "add";                                  //$NON-NLS-1$
	/** Constant for the branch action. */
	public static final String ACTION_BRANCH = "branch";                               //$NON-NLS-1$
	/** Constant for the delete action. */
	public static final String ACTION_DELETE = "delete";                               //$NON-NLS-1$
	/** Constant of the edit action. */
	public static final String ACTION_EDIT   = "edit";                                 //$NON-NLS-1$

	// ----------------------------------------------------------------------------------- Constants

	/** serial number of the class. */	
	static final long serialVersionUID = -5903762684861295448L;

	// --------------------------------------------------------------------------- Private Variables

	/** Stores the magic number. Used to distingusih between null info. */
	private Integer magic = new Integer(0x0001);
	/** Stores a creation timestamp. */
	private long timestamp = System.currentTimeMillis();
	/** Stores the action. */
	private String action = "";                                                        //$NON-NLS-1$
	/** Stores the client filename. */
	private String clientFileName = "";                                                //$NON-NLS-1$
	/** Stores the depot filename. */
	private String depotFileName = "";                                                 //$NON-NLS-1$
	/** Stores the head action. */
	private String headAction = "";                                                    //$NON-NLS-1$
	/** Stores the head type. */
	private String headType = "";                                                      //$NON-NLS-1$
	/** Stores the head time. */
	private Date headTime = new Date();
	/** Stores the head revision number. */
	private Integer headRev = new Integer(0);
	/** Stores the head changelist number. */
	private Integer headChange = new Integer(0);
	/** Stores the client changelist number. */
	private Integer change;
	/** Stores the have revision. */
	private Integer haveRev = new Integer(0);
	/** Indicates whether the file is resolved. */
	private Boolean resolved = Boolean.TRUE;
	/** Indicates whether the file is also opened by some other person. */
	private Boolean otherOpen = Boolean.FALSE;
	/** Indicates that the resource is locked by "us". */
	private Boolean ourLock = Boolean.FALSE;

	// ------------------------------------------------------------------------- Public Constructors

	/** Constructor for ResourceSyncInfo. */
	public ResourceSyncInfo()
	{
	}
	
	// ------------------------------------------------------------------------ Private Constructors
	
	/** Constructor for ResourceSyncInfo.
	  * 
	  * @param magic the magic number.
	  */
	private ResourceSyncInfo(int magic)
	{
		this.magic = new Integer(magic);
	}

	// ------------------------------------------------------------------------------ Public Methods
	
	/** Returns the entry's timestamp.
	  *
	  * @return the timestamp. 
	  */ 
	public long getTimestamp()
	{
		return timestamp;
	}
	
	/** Returns the magic number.
	  * 
	  * @return the magic number.
	  */
	public int getMagic()
	{
		return magic.intValue();
	}

	/** Gets the action.
	  * 
	  * @return Returns a String
	  */
	public String getAction()
	{
		return action;
	}

	/** Sets the action.
	  * 
	  * @param action The action to set
	  */
	public void setAction(String action)
	{
		Policy.assertNotNull(action);
		this.action = action.intern();
	}

	/** Gets the depotFileName.
	  * 
	  * @return Returns a String
	  */
	public String getClientFileName()
	{
		return clientFileName;
	}

	/** Sets the clientFileName.
	  * 
	  * @param clientFileName The clientFileName to set
	  */
	public void setClientFileName(String clientFileName)
	{
		Policy.assertNotNull(clientFileName);
		this.clientFileName = clientFileName;
	}

	/** Gets the depotFileName.
	  * 
	  * @return Returns a String
	  */
	public String getDepotFileName()
	{
		return depotFileName;
	}

	/** Sets the depotFileName.
	  * 
	  * @param depotFileName The depotFileName to set
	  */
	public void setDepotFileName(String depotFileName)
	{
		Policy.assertNotNull(depotFileName);
		this.depotFileName = depotFileName;
	}

	/** Gets the headAction.
	  * 
	  * @return the head action.
	  */	
	public String getHeadAction()
	{
		return headAction;
	}
	
	/** Sets the headAction.
	  * 
	  * @param headAction The headAction to set
	  */
	public void setHeadAction(String headAction)
	{
		Policy.assertNotNull(headAction);
		this.headAction = headAction.intern();
	}
	
	/** Gets the headType.
	  * 
	  * @return Returns a String
	  */
	public String getHeadType()
	{
		return headType;
	}

	/** Sets the headType.
	  * 
	  * @param headType The headType to set
	  */
	public void setHeadType(String headType)
	{
		Policy.assertNotNull(headType);		
		this.headType = headType;
	}

	/** Gets the headRev.
	  * @return Returns a Integer
	  */
	public Integer getHeadRev()
	{
		return headRev;
	}

	/** Sets the headRev.
	  * 
	  * @param headRev The headRev to set
	  */
	public void setHeadRev(Integer headRev)
	{
		Policy.assertNotNull(headRev);
		this.headRev = headRev;
	}

	/** Gets the headChange.
	  * 
	  * @return Returns a Integer
	  */
	public Integer getHeadChange()
	{
		return headChange;
	}

	/** Sets the headChange.
	  * 
	  * @param headChange The headChange to set
	  */
	public void setHeadChange(Integer headChange)
	{
		Policy.assertNotNull(headChange);		
		this.headChange = headChange;
	}
	
	/** Gets the change.
	  *
	  * @return return an Integer. 
	  */
	public Integer getChange()
	{
		return change;
	}
	
	/** Sets the change.
	  *
	  * @param change the new change value. 
	  */
	public void setChange(Integer change)
	{		
		this.change = change;
	}

	/** Gets the haveRev.
	  * 
	  * @return Returns a Integer
	  */
	public Integer getHaveRev()
	{
		return haveRev;
	}

	/** Sets the haveRev.
	  * 
	  * @param haveRev The haveRev to set
	  */
	public void setHaveRev(Integer haveRev)
	{
		Policy.assertNotNull(haveRev);		
		this.haveRev = haveRev;
	}

	/** Gets the head time.
	  * 	
	  * @return the head time.
	  */
	public Date getHeadTime()
	{
		return headTime;
	}
	
	/** Sets the head time.
	  * 
	  * @param headTime the new head time.
	  */
	public void setHeadTime(Date headTime)
	{
		Policy.assertNotNull(headTime);
		this.headTime = headTime;
	}

	/** Checks whether the file is resolved.
	  * 
	  * @return true if resolved.
	  */
	public Boolean isResolved()
	{
		return resolved;
	}
	
	/** Sets whether the file is resolved.
	  * 
	  * @param resolved the new resolved value.
	  */
	public void setResolved(Boolean resolved)
	{
		this.resolved = resolved;
	}

	/** Checks whether the file is opened by another one.
	  * 
	  * @return true or false.
	  */
	public boolean isOtherOpen()
	{
		return otherOpen.booleanValue(); 
	}
	
	/** Sets the otherOpen value.
	  * 
	  * @param theOtherOpen true or false.
	  */
	public void setOtherOpen(boolean theOtherOpen)
	{
		otherOpen = theOtherOpen ? Boolean.TRUE : Boolean.FALSE;
	}

	/** Returns if the resource is locked by us or not.
	  *
	  * @return true or false. 
	  */
	public boolean hasOurLock()
	{
		return ourLock.booleanValue();
	}
	
	/** Changes the value that indicates whether the resource is locked locally
	  * or not.
	  * 
	  * @param newOurLock new value 
	  */
	public void setHasOurLock(boolean newOurLock)
	{
		ourLock = newOurLock ? Boolean.TRUE : Boolean.FALSE;
	}

	/** Checks whether this is a NULL resource sync info. 
	  * 
	  * @return true if this is a null info.
	  */
	public boolean isNull()
	{
		return magic.equals(NULL_INFO.magic);
	}	

	/** Returns the name of the resource.
	  * 
	  * @return the resource's name.
	  */
	public String getName()
	{
		String name = getClientFileName();
		return name.substring(name.lastIndexOf(File.separator) + 1);
	}
	
	/** Checks whether the resource is deleted. 
	  * 
	  * @return true if resource is deleted.
	  */
	public boolean isDeleted()
	{
		if (action.equals(ACTION_ADD))
		{
			return false;
		}
		
		return haveRev.intValue() == 0 ||
		       (headRev.equals(haveRev) && headAction.equals(ACTION_DELETE) && action.equals(""));
	}
	
	// ------------------------------------------------------------- Public Methods (Externalizable)
	
	/** @see java.io.Externalizable#readExternal(java.io.ObjectInput) */
	public void readExternal(ObjectInput in) 
		throws IOException 
	{
		magic = new Integer(in.readInt());
		timestamp = in.readLong();
		action = readAction(in);
		clientFileName = in.readUTF();
		depotFileName = in.readUTF();
		headAction = readAction(in);
		headType = in.readUTF();
		headTime = new Date(in.readLong());
		headRev = new Integer(in.readInt());
		headChange = new Integer(in.readInt());
		
		int tempInt = in.readInt();
		change = (tempInt != -1) ? new Integer(tempInt) : null;
		haveRev = new Integer(in.readInt());
		resolved = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
		otherOpen = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
		ourLock = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
	}

	/** @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) */
	public void writeExternal(ObjectOutput out) 
		throws IOException 
	{
		out.writeInt(magic.intValue());
		out.writeLong(timestamp);
		writeAction(out, action);
		out.writeUTF(clientFileName);
		out.writeUTF(depotFileName);
		writeAction(out, headAction);
		out.writeUTF(headType);
		out.writeLong(headTime.getTime());
		out.writeInt(headRev.intValue());
		out.writeInt(headChange.intValue());
		out.writeInt((change != null) ? change.intValue() : -1);
		out.writeInt(haveRev.intValue());
		out.writeBoolean(resolved.booleanValue());
		out.writeBoolean(otherOpen.booleanValue());
		out.writeBoolean(ourLock.booleanValue());
	}
	
	// ----------------------------------------------------------------------------- Private Methods
	
	/** Called to read the encoded form of an action.
	  *
	  * @param in the object used for input.
	  * @return the action as String.
	  * 
	  * @throws IOException in case of an I/O error.
	  */
	private String readAction(ObjectInput in)
		throws IOException
	{
		byte b = in.readByte();
		switch (b)
		{
			case 0:
				return ACTION_ADD;
			
			case 1:
				return ACTION_EDIT;
				
			case 2:
				return ACTION_DELETE;
				
			case 3:
				return "";
			
			case 4:
				return in.readUTF().intern();
				
			case 5:
				return ACTION_BRANCH;
		}
		
		throw new IllegalStateException("Unsupported action type.");                   //$NON-NLS-1$
	}
	
	/** Called to write the action in an encoded form.
	  *
	  * @param out the object used for output.
	  * @param action the action.
	  * 
	  * @throws IOException in case of an I/O error. 
	  */
	private void writeAction(ObjectOutput out, String action) 
		throws IOException
	{
		if (action.equals(ACTION_ADD))
		{
			out.writeByte(0);
		}
		else
		if (action.equals(ACTION_EDIT))
		{
			out.writeByte(1);
		}
		else
		if (action.equals(ACTION_DELETE))
		{
			out.writeByte(2);
		}
		else
		if (action.equals(""))
		{
			out.writeByte(3);
		}
		else
		if (action.equals(ACTION_BRANCH))
		{
			out.writeByte(5);
		}
		else
		{
			out.writeByte(4);
			out.writeUTF(action);
		}
	}
}
